home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / lib / xulrunner-1.9.0.14 / chrome / cview.jar / content / cview / tree-utils.js < prev   
Encoding:
JavaScript  |  2006-06-06  |  33.1 KB  |  1,260 lines

  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2.  *
  3.  * ***** BEGIN LICENSE BLOCK *****
  4.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  5.  *
  6.  * The contents of this file are subject to the Mozilla Public License Version
  7.  * 1.1 (the "License"); you may not use this file except in compliance with
  8.  * the License. You may obtain a copy of the License at
  9.  * http://www.mozilla.org/MPL/
  10.  *
  11.  * Software distributed under the License is distributed on an "AS IS" basis,
  12.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  13.  * for the specific language governing rights and limitations under the
  14.  * License.
  15.  *
  16.  * The Original Code is The JavaScript Debugger.
  17.  *
  18.  * The Initial Developer of the Original Code is
  19.  * Netscape Communications Corporation.
  20.  * Portions created by the Initial Developer are Copyright (C) 1998
  21.  * the Initial Developer. All Rights Reserved.
  22.  *
  23.  * Contributor(s):
  24.  *   Robert Ginda, <rginda@netscape.com>, original author
  25.  *
  26.  * Alternatively, the contents of this file may be used under the terms of
  27.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  28.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  29.  * in which case the provisions of the GPL or the LGPL are applicable instead
  30.  * of those above. If you wish to allow use of your version of this file only
  31.  * under the terms of either the GPL or the LGPL, and not to allow others to
  32.  * use your version of this file under the terms of the MPL, indicate your
  33.  * decision by deleting the provisions above and replace them with the notice
  34.  * and other provisions required by the GPL or the LGPL. If you do not delete
  35.  * the provisions above, a recipient may use your version of this file under
  36.  * the terms of any one of the MPL, the GPL or the LGPL.
  37.  *
  38.  * ***** END LICENSE BLOCK ***** */
  39.  
  40. /*
  41.  * BasicOView provides functionality of tree whose elements have no children.
  42.  * Usage:
  43.  * var myTree = new BasicOView()
  44.  * myTree.setColumnNames (["col 1", "col 2"]);
  45.  * myTree.data = [["row 1, col 1", "row 1, col 2"],
  46.  *                    ["row 2, col 1", "row 2, col 2"]];
  47.  * { override get*Properties, etc, as suits your purpose. }
  48.  *
  49.  * treeBoxObject.view = myTree;
  50.  * 
  51.  * You'll need to make the appropriate myTree.tree.invalidate calls
  52.  * when myTree.data changes.
  53.  */
  54.  
  55. function BasicOView()
  56. {}
  57.  
  58. /* functions *you* should call to initialize and maintain the tree state */
  59.  
  60. /* scroll the line specified by |line| to the center of the tree */
  61. BasicOView.prototype.centerLine =
  62. function bov_ctrln (line)
  63. {
  64.     var first = this.tree.getFirstVisibleRow();
  65.     var last = this.tree.getLastVisibleRow();
  66.     this.scrollToRow(line - total / 2);
  67. }
  68.  
  69. /* call this to set the association between column names and data columns */
  70. BasicOView.prototype.setColumnNames =
  71. function bov_setcn (aryNames)
  72. {
  73.     this.columnNames = new Object();
  74.     for (var i = 0; i < aryNames.length; ++i)
  75.         this.columnNames[aryNames[i]] = i;
  76. }
  77.  
  78. /*
  79.  * scroll the source so |line| is at either the top, center, or bottom
  80.  * of the view, delepding on the value of |align|.
  81.  *
  82.  * line is the one based target line.
  83.  * if align is negative, the line will be scrolled to the top, if align is
  84.  * zero the line will be centered, and if align is greater than 0 the line
  85.  * will be scrolled to the bottom.  0 is the default.
  86.  */
  87. BasicOView.prototype.scrollTo =
  88. function bov_scrollto (line, align)
  89. {
  90.     var headerRows = 1;
  91.     
  92.     var first = this.tree.getFirstVisibleRow();
  93.     var last  = this.tree.getLastVisibleRow();
  94.     var viz   = last - first - headerRows; /* total number of visible rows */
  95.  
  96.     /* all rows are visible, nothing to scroll */
  97.     if (first == 0 && last > this.rowCount)
  98.     {
  99.         dd ("scrollTo: view does not overflow");
  100.         return;
  101.     }
  102.     
  103.     /* tree lines are 0 based, we accept one based lines, deal with it */
  104.     --line;
  105.  
  106.     /* safety clamp */
  107.     if (line < 0)
  108.         line = 0;
  109.     if (line > this.rowCount)
  110.         line = this.rowCount;    
  111.  
  112.     if (align < 0)
  113.     {
  114.         if (line > this.rowCount - viz) /* overscroll, can't put a row from */
  115.             line = this.rowCount - viz; /* last page at the top. */
  116.         this.tree.scrollToRow(line);
  117.         return;
  118.     }
  119.     else if (align > 0)
  120.     {
  121.         if (line < viz) /* underscroll, can't put a row from the first page at */
  122.             line = 0;   /* the bottom. */
  123.         else
  124.             line = line - total_viz + headerRows;
  125.         
  126.         this.tree.scrollToRow(line);
  127.     }
  128.     else
  129.     {
  130.         var half_viz = viz / 2;
  131.         /* lines past this line can't be centered without causing the tree
  132.          * to show more rows than we have. */
  133.         var lastCenterable = this.rowCount - half_viz;
  134.         if (line > lastCenterable)
  135.             line = lastCenterable;
  136.         /* lines before this can't be centered without causing the tree
  137.          * to attempt to display negative rows. */
  138.         else if (line < half_viz)
  139.             line = half_viz;
  140.         this.tree.scrollToRow(line - half_viz);
  141.     }                    
  142. }       
  143.  
  144. /*
  145.  * functions the tree will call to retrieve the list state (nsITreeView.)
  146.  */
  147.  
  148. BasicOView.prototype.rowCount = 0;
  149.  
  150. BasicOView.prototype.selection = null;
  151.  
  152. BasicOView.prototype.getCellProperties =
  153. function bov_cellprops (row, col, properties)
  154. {}
  155.  
  156. BasicOView.prototype.getColumnProperties =
  157. function bov_colprops (col, properties)
  158. {}
  159.  
  160. BasicOView.prototype.getRowProperties =
  161. function bov_rowprops (index, properties)
  162. {}
  163.  
  164. BasicOView.prototype.isContainer =
  165. function bov_isctr (index)
  166. {
  167.     return false;
  168. }
  169.  
  170. BasicOView.prototype.isContainerOpen =
  171. function bov_isctropen (index)
  172. {
  173.     return false;
  174. }
  175.  
  176. BasicOView.prototype.isContainerEmpty =
  177. function bov_isctrempt (index)
  178. {
  179.     return false;
  180. }
  181.  
  182. BasicOView.prototype.isSeparator =
  183. function bov_iscontainer (index)
  184. {
  185.     return false;
  186. }
  187.  
  188. BasicOView.prototype.isSorted =
  189. function bov_issorted (index)
  190. {
  191.     return false;
  192. }
  193.  
  194. BasicOView.prototype.canDrop =
  195. function bov_drop (index, orientation)
  196. {
  197.     return false;
  198. }
  199.  
  200. BasicOView.prototype.drop =
  201. function bov_drop (index, orientation)
  202. {
  203.     return false;
  204. }
  205.  
  206. BasicOView.prototype.getParentIndex =
  207. function bov_getpi (index)
  208. {
  209.     return 0;
  210. }
  211.  
  212. BasicOView.prototype.hasNextSibling =
  213. function bov_hasnxtsib (rowIndex, afterIndex)
  214. {
  215.     return (afterIndex < (this.rowCount - 1));
  216. }
  217.  
  218. BasicOView.prototype.getLevel =
  219. function bov_getlvl (index)
  220. {
  221.     return 0;
  222. }
  223.  
  224. BasicOView.prototype.getImageSrc =
  225. function bov_getimgsrc (row, col)
  226. {
  227. }
  228.  
  229. BasicOView.prototype.getProgressMode =
  230. function bov_getprgmode (row, col)
  231. {
  232. }
  233.  
  234. BasicOView.prototype.getCellValue =
  235. function bov_getcellval (row, col)
  236. {
  237. }
  238.  
  239. BasicOView.prototype.getCellText =
  240. function bov_getcelltxt (row, col)
  241. {
  242.     if (!this.columnNames)
  243.         return "";
  244.     
  245.     var colName = this.columnNames[col.id];
  246.     
  247.     if (typeof colName == "undefined")
  248.         return "";
  249.     
  250.     return this.data[row][colName];
  251. }
  252.  
  253. BasicOView.prototype.setTree =
  254. function bov_seto (tree)
  255. {
  256.     this.tree = tree;
  257. }
  258.  
  259. BasicOView.prototype.toggleOpenState =
  260. function bov_toggleopen (index)
  261. {
  262. }
  263.  
  264. BasicOView.prototype.cycleHeader =
  265. function bov_cyclehdr (col)
  266. {
  267. }
  268.  
  269. BasicOView.prototype.selectionChanged =
  270. function bov_selchg ()
  271. {
  272. }
  273.  
  274. BasicOView.prototype.cycleCell =
  275. function bov_cyclecell (row, col)
  276. {
  277. }
  278.  
  279. BasicOView.prototype.isEditable =
  280. function bov_isedit (row, col)
  281. {
  282.     return false;
  283. }
  284.  
  285. BasicOView.prototype.isSelectable =
  286. function bov_isselect (row, col)
  287. {
  288.     return false;
  289. }
  290.  
  291. BasicOView.prototype.setCellValue =
  292. function bov_setct (row, col, value)
  293. {
  294. }
  295.  
  296. BasicOView.prototype.setCellText =
  297. function bov_setct (row, col, value)
  298. {
  299. }
  300.  
  301. BasicOView.prototype.performAction =
  302. function bov_pact (action)
  303. {
  304. }
  305.  
  306. BasicOView.prototype.performActionOnRow =
  307. function bov_pactrow (action)
  308. {
  309. }
  310.  
  311. BasicOView.prototype.performActionOnCell =
  312. function bov_pactcell (action)
  313. {
  314. }
  315.  
  316. /*
  317.  * record for the TreeOView.  these things take care of keeping the TreeOView
  318.  * properly informed of changes in value and child count.  you shouldn't have
  319.  * to maintain tree state at all.
  320.  *
  321.  * |share| should be an otherwise empty object to store cache data.
  322.  * you should use the same object as the |share| for the TreeOView that you
  323.  * indend to contain these records.
  324.  *
  325.  */
  326. function TreeOViewRecord(share)
  327. {
  328.     this._share = share;
  329.     this.visualFootprint = 1;
  330.     this.isHidden = true; /* records are considered hidden until they are
  331.                            * inserted into a live tree */
  332. }
  333.  
  334. /*
  335.  * walk the parent tree to find our tree container.  return null if there is
  336.  * none
  337.  */
  338. TreeOViewRecord.prototype.findContainerTree =
  339. function tovr_gettree ()
  340. {
  341.     var parent = this.parentRecord
  342.     
  343.     while (parent)
  344.     {
  345.         if ("_treeView" in parent)
  346.             return parent._treeView;
  347.         parent = parent.parentRecord
  348.     }
  349.  
  350.     return null;
  351. }
  352.  
  353. /* count the number of parents, not including the root node */
  354. TreeOViewRecord.prototype.__defineGetter__("level", tovr_getLevel);
  355. function tovr_getLevel ()
  356. {
  357.     var rv = 0;
  358.     var parentRecord = this.parentRecord;
  359.     while ("parentRecord" in parentRecord &&
  360.            (parentRecord = parentRecord.parentRecord)) ++rv;
  361.     return rv;
  362. }
  363.  
  364. /*
  365.  * associates a property name on this record, with a column in the tree.  This
  366.  * method will set up a get/set pair for the property name you specify which
  367.  * will take care of updating the tree when the value changes.  DO NOT try
  368.  * to change your mind later.  Do not attach a different name to the same colID,
  369.  * and do no rename the colID.  You have been warned.
  370.  */
  371. TreeOViewRecord.prototype.setColumnPropertyName =
  372. function tovr_setcol (colID, propertyName)
  373. {
  374.     function tovr_getValueShim ()
  375.     {
  376.         return this._colValues[colID];
  377.     }
  378.     function tovr_setValueShim (newValue)
  379.     {
  380.         this._colValues[colID] = newValue;
  381.         /* XXX this.invalidate(); */
  382.         return newValue;
  383.     }
  384.  
  385.     if (!("_colValues" in this))
  386.         this._colValues = new Object();
  387.     
  388.     if (typeof propertyName == "function")
  389.     {
  390.         this._colValues.__defineGetter__(colID, propertyName);
  391.     }
  392.     else
  393.     {
  394.         this.__defineGetter__(propertyName, tovr_getValueShim);
  395.         this.__defineSetter__(propertyName, tovr_setValueShim);
  396.     }
  397. }
  398.  
  399. /*
  400.  * set the default sort column and resort.
  401.  */
  402. TreeOViewRecord.prototype.setSortColumn =
  403. function tovr_setcol (colID, dir)
  404. {
  405.     //dd ("setting sort column to " + colID);
  406.     this._share.sortColumn = colID;
  407.     this._share.sortDirection = (typeof dir == "undefined") ? 1 : dir;
  408.     this.resort();
  409. }
  410.  
  411. /*
  412.  * set the default sort direction.  1 is ascending, -1 is descending, 0 is no
  413.  * sort.  setting this to 0 will *not* recover the natural insertion order,
  414.  * it will only affect newly added items.
  415.  */
  416. TreeOViewRecord.prototype.setSortDirection =
  417. function tovr_setdir (dir)
  418. {
  419.     this._share.sortDirection = dir;
  420. }
  421.  
  422. /*
  423.  * invalidate this row in the tree
  424.  */
  425. TreeOViewRecord.prototype.invalidate =
  426. function tovr_invalidate()
  427. {
  428.     var tree = this.findContainerTree();
  429.     if (tree)
  430.     {
  431.         var row = this.calculateVisualRow();
  432.         if (row != -1)
  433.             tree.tree.invalidateRow(row);
  434.     }
  435. }
  436.  
  437. /*
  438.  * invalidate any data in the cache.
  439.  */
  440. TreeOViewRecord.prototype.invalidateCache =
  441. function tovr_killcache()
  442. {
  443.     this._share.rowCache = new Object();
  444.     this._share.lastComputedIndex = -1;
  445.     this._share.lastIndexOwner = null;
  446. }
  447.  
  448. /*
  449.  * default comparator function for sorts.  if you want a custom sort, override
  450.  * this method.  We declare tovr_sortcmp as a top level function, instead of
  451.  * a function expression so we can refer to it later.
  452.  */
  453. TreeOViewRecord.prototype.sortCompare = tovr_sortcmp;
  454. function tovr_sortcmp (a, b)
  455. {
  456.     var sc = a._share.sortColumn;
  457.     var sd = a._share.sortDirection;    
  458.     
  459.     a = a[sc];
  460.     b = b[sc];
  461.  
  462.     if (a < b)
  463.         return -1 * sd;
  464.     
  465.     if (a > b)
  466.         return 1 * sd;
  467.     
  468.     return 0;
  469. }
  470.  
  471. /*
  472.  * this method will cause all child records to be resorted.  any records
  473.  * with the default sortCompare method will be sorted by the colID passed to
  474.  * setSortColumn.
  475.  *
  476.  * the local parameter is used internally to control whether or not the 
  477.  * sorted rows are invalidated.  don't use it yourself.
  478.  */
  479. TreeOViewRecord.prototype.resort =
  480. function tovr_resort (leafSort)
  481. {
  482.     if (!("childData" in this) || this.childData.length < 1 ||
  483.         (this.childData[0].sortCompare == tovr_sortcmp &&
  484.          !("sortColumn" in this._share) || this._share.sortDirection == 0))
  485.     {
  486.         /* if we have no children, or we have the default sort compare and no
  487.          * sort flags, then just exit */
  488.         return;
  489.     }
  490.  
  491.     this.childData.sort(this.childData[0].sortCompare);
  492.     
  493.     for (var i = 0; i < this.childData.length; ++i)
  494.     {
  495.         this.childData[i].childIndex = i;
  496.         if ("isContainerOpen" in this.childData[i] &&
  497.             this.childData[i].isContainerOpen)
  498.             this.childData[i].resort(true);
  499.         else
  500.             this.childData[i].sortIsInvalid = true;
  501.     }
  502.     
  503.     if (!leafSort)
  504.     {
  505.         this.invalidateCache();
  506.         var tree = this.findContainerTree();
  507.         if (tree && tree.tree)
  508.         {
  509.             var rowIndex = this.calculateVisualRow();
  510.             /*
  511.             dd ("invalidating " + rowIndex + " - " +
  512.                 (rowIndex + this.visualFootprint - 1));
  513.             */
  514.             tree.tree.invalidateRange (rowIndex,
  515.                                        rowIndex + this.visualFootprint - 1);
  516.         }
  517.     }
  518.     /*
  519.     else
  520.         dd("not a leafSort");
  521.     */
  522.     delete this.sortIsInvalid;
  523. }
  524.     
  525. /*
  526.  * call this to indicate that this node may have children at one point.  make
  527.  * sure to call it before adding your first child.
  528.  */
  529. TreeOViewRecord.prototype.reserveChildren =
  530. function tovr_rkids ()
  531. {
  532.     if (!("childData" in this))
  533.         this.childData = new Array();
  534.     if (!("isContainerOpen" in this))
  535.         this.isContainerOpen = false;
  536. }
  537.  
  538. /*
  539.  * add a child to the end of the child list for this record.  takes care of 
  540.  * updating the tree as well.
  541.  */
  542. TreeOViewRecord.prototype.appendChild =
  543. function tovr_appchild (child)
  544. {
  545.     if (!(child instanceof TreeOViewRecord))
  546.         throw Components.results.NS_ERROR_INVALID_PARAM;
  547.  
  548.     var changeStart = (this.childData.length > 0) ?
  549.         this.childData[this.childData.length - 1].calculateVisualRow() :
  550.         this.calculateVisualRow();
  551.     
  552.     child.isHidden = false;
  553.     child.parentRecord = this;
  554.     child.childIndex = this.childData.length;
  555.     this.childData.push(child);
  556.     
  557.     if ("isContainerOpen" in this && this.isContainerOpen)
  558.     {
  559.         if (this.calculateVisualRow() >= 0)
  560.         {
  561.             this.resort(true);  /* resort, don't invalidate.  we're going to do
  562.                                  * that in the onVisualFootprintChanged call. */
  563.         }
  564.         this.onVisualFootprintChanged(changeStart, child.visualFootprint);
  565.     }
  566. }
  567.  
  568. /*
  569.  * add a list of children to the end of the child list for this record.
  570.  * faster than multiple appendChild() calls.
  571.  */
  572. TreeOViewRecord.prototype.appendChildren =
  573. function tovr_appchild (children, skipResort)
  574. {
  575.     var changeStart = (this.childData.length > 0) ?
  576.         this.childData[this.childData.length - 1].calculateVisualRow() :
  577.         this.calculateVisualRow();
  578.  
  579.     var start = this.childData.length;
  580.     var delta = 0;
  581.     var len = children.length;
  582.  
  583.     for (var i = 0; i <  len; ++i)
  584.     {
  585.         var child = children[i];
  586.         child.isHidden = false;
  587.         child.parentRecord = this;
  588.         this.childData[start + i] = child;
  589.         child.childIndex = i;
  590.         delta += child.visualFootprint;
  591.     }
  592.     
  593.     if ("isContainerOpen" in this && this.isContainerOpen)
  594.     {
  595.         if (!skipResort && this.calculateVisualRow() >= 0)
  596.         {
  597.             this.resort(true);  /* resort, don't invalidate.  we're going to do
  598.                                  * that in the onVisualFootprintChanged call. */
  599.         }
  600.         this.onVisualFootprintChanged(changeStart, delta);
  601.     }
  602. }
  603.  
  604. /*
  605.  * remove a child from this record. updates the tree too.  DON'T call this with
  606.  * an index not actually contained by this record.
  607.  */
  608. TreeOViewRecord.prototype.removeChildAtIndex =
  609. function tovr_remchild (index)
  610. {
  611.     for (var i = index + 1; i < this.childData.length; ++i)
  612.         --this.childData[i].childIndex;
  613.     
  614.     var fpDelta = -this.childData[index].visualFootprint;
  615.     var changeStart = this.childData[index].calculateVisualRow();
  616.     this.childData[index].childIndex = -1;
  617.     delete this.childData[index].parentRecord;
  618.     arrayRemoveAt (this.childData, index);
  619.     this.invalidateCache();
  620.     this.onVisualFootprintChanged (changeStart, fpDelta);
  621. }
  622.  
  623. /*
  624.  * hide this record and all descendants.
  625.  */
  626. TreeOViewRecord.prototype.hide =
  627. function tovr_hide ()
  628. {
  629.     if (this.isHidden)
  630.         return;
  631.  
  632.     /* get the row before hiding */
  633.     var row = this.calculateVisualRow();
  634.     this.invalidateCache();
  635.     this.isHidden = true;
  636.     /* go right to the parent so we don't muck with our own visualFoorptint
  637.      * record, we'll need it to be correct if we're ever unHidden. */
  638.     this.parentRecord.onVisualFootprintChanged (row, -this.visualFootprint);
  639. }
  640.  
  641. /*
  642.  * unhide this record and all descendants.
  643.  */
  644. TreeOViewRecord.prototype.unHide =
  645. function tovr_uhide ()
  646. {
  647.     if (!this.isHidden)
  648.         return;
  649.  
  650.     this.isHidden = false;
  651.     this.invalidateCache();
  652.     var row = this.calculateVisualRow();
  653.     this.parentRecord.onVisualFootprintChanged (row, this.visualFootprint);
  654. }
  655.  
  656. /*
  657.  * open this record, exposing it's children.  DON'T call this method if the record
  658.  * has no children.
  659.  */
  660. TreeOViewRecord.prototype.open =
  661. function tovr_open ()
  662. {
  663.     if (this.isContainerOpen)
  664.         return;
  665.  
  666.     if ("onPreOpen" in this)
  667.         this.onPreOpen();
  668.     
  669.     this.isContainerOpen = true;
  670.     var delta = 0;
  671.     for (var i = 0; i < this.childData.length; ++i)
  672.     {
  673.         if (!this.childData[i].isHidden)
  674.             delta += this.childData[i].visualFootprint;
  675.     }
  676.  
  677.     this.resort(true);
  678.     this.visualFootprint += delta;
  679.     if ("parentRecord" in this)
  680.         this.parentRecord.onVisualFootprintChanged(this.calculateVisualRow(),
  681.                                                    delta);
  682. }
  683.  
  684. /*
  685.  * close this record, hiding it's children.  DON'T call this method if the record
  686.  * has no children, or if it is already closed.
  687.  */
  688. TreeOViewRecord.prototype.close =
  689. function tovr_close ()
  690. {
  691.     if (!this.isContainerOpen)
  692.         return;
  693.     
  694.     this.isContainerOpen = false;
  695.     var delta = 1 - this.visualFootprint;
  696.     this.visualFootprint += delta;
  697.     if (this.parentRecord)
  698.         this.parentRecord.onVisualFootprintChanged(this.calculateVisualRow(),
  699.                                                    delta);
  700.     if (this.onPostClose)
  701.         this.onPostClose();
  702. }
  703.  
  704. /*
  705.  * called when a node above this one grows or shrinks.  we need to adjust
  706.  * our own visualFootprint to match the change, and pass the message on.
  707.  */
  708. TreeOViewRecord.prototype.onVisualFootprintChanged =
  709. function tovr_vpchange (start, amount)
  710. {
  711.     /* if we're not hidden, but this notification came from a hidden node 
  712.      * (start == -1), ignore it, it doesn't affect us. */
  713.     if (start == -1 && !this.isHidden)
  714.     {
  715.         
  716.         //dd ("vfp change (" + amount + ") from hidden node ignored.");
  717.         return;
  718.     }
  719.     
  720.     this.visualFootprint += amount;
  721.  
  722.     if ("parentRecord" in this)
  723.         this.parentRecord.onVisualFootprintChanged(start, amount);
  724. }
  725.  
  726. /*
  727.  * calculate the "visual" row for this record.  If the record isn't actually
  728.  * visible return -1.
  729.  * eg.
  730.  * Name        Visual Row
  731.  * node1       0
  732.  *   node11    1
  733.  *   node12    2
  734.  * node2       3
  735.  *   node21    4
  736.  * node3       5
  737.  */
  738. TreeOViewRecord.prototype.calculateVisualRow =
  739. function tovr_calcrow ()
  740. {
  741.     /* if this is the second time in a row that someone asked us, fetch the last
  742.      * result from the cache. */
  743.     if (this._share.lastIndexOwner == this)
  744.         return this._share.lastComputedIndex;
  745.  
  746.     var vrow;
  747.  
  748.         /* if this is an uninserted or hidden node, or... */
  749.     if (!("parentRecord" in this) || (this.isHidden) ||
  750.         /* if parent isn't open, or... */
  751.         (!this.parentRecord.isContainerOpen) ||
  752.         /* parent isn't visible */
  753.         ((vrow = this.parentRecord.calculateVisualRow()) == -1))
  754.     {
  755.         /* then we're not visible, return -1 */
  756.         //dd ("cvr: returning -1");
  757.         return -1;
  758.     }
  759.  
  760.     /* parent is the root node XXX parent is not visible */
  761.     if (vrow == null)
  762.         vrow = 0;
  763.     else
  764.     /* parent is not the root node, add one for the space they take up. */
  765.         ++vrow;
  766.  
  767.     /* add in the footprint for all of the earlier siblings */
  768.     for (var i = 0; i < this.childIndex; ++i)
  769.     {
  770.         if (!this.parentRecord.childData[i].isHidden)
  771.             vrow += this.parentRecord.childData[i].visualFootprint;
  772.     }
  773.  
  774.     /* save this calculation to the cache. */
  775.     this._share.lastIndexOwner = this;
  776.     this._share.lastComputedIndex = vrow;
  777.  
  778.     //@DEBUG-cvr dd ("cvr: returning " + vrow);
  779.     return vrow;
  780. }
  781.  
  782. /*
  783.  * locates the child record for the visible row |targetRow|.  DO NOT call this
  784.  * with a targetRow less than this record's visual row, or greater than this
  785.  * record's visual row + the number of visible children it has.
  786.  */
  787. TreeOViewRecord.prototype.locateChildByVisualRow =
  788. function tovr_find (targetRow, myRow)
  789. {
  790.     if (targetRow in this._share.rowCache)
  791.         return this._share.rowCache[targetRow];
  792.  
  793.     else if (0) {
  794.         /* XXX take this out later */
  795.         if (typeof myRow == "undefined")
  796.             myRow = this.calculateVisualRow();
  797.         else
  798.         {
  799.             ASSERT (myRow == this.calculateVisualRow(), "someone lied to me, " +
  800.                     myRow + " != " + this.calculateVisualRow());
  801.         }
  802.     
  803.         if (targetRow < myRow || targetRow > myRow + this.visualFootprint)
  804.         {
  805.             ASSERT (0, "I don't contain visual row " + targetRow + ", only " +
  806.                     myRow + "..." + (myRow + this.visualFootprint));
  807.             return null;
  808.         }
  809.     }
  810.  
  811.     /* if this is true, we *are* the index */
  812.     if (targetRow == myRow)
  813.         return (this._share.rowCache[targetRow] = this);
  814.  
  815.     /* otherwise, we've got to search the kids */
  816.     var childStart = myRow; /* childStart represents the starting visual row
  817.                              * for the child we're examining. */
  818.     for (var i = 0; i < this.childData.length; ++i)
  819.     {
  820.         var child = this.childData[i];
  821.         /* ignore hidden children */
  822.         if (child.isHidden)
  823.             continue;
  824.         /* if this kid is the targetRow, we're done */
  825.         if (childStart == targetRow)
  826.             return (this._share.rowCache[targetRow] = child);
  827.         /* if this kid contains the index, ask *it* to find the record */
  828.         else if (targetRow <= childStart + child.visualFootprint) {
  829.             /* this *has* to succeed */
  830.             var rv = child.locateChildByVisualRow(targetRow, childStart + 1);
  831.             //XXXASSERT (rv, "Can't find a row that *has* to be there.");
  832.             /* don't cache this, the previous call to locateChildByVisualRow
  833.              * just did. */
  834.             return rv;
  835.         }
  836.  
  837.         /* otherwise, get ready to ask the next kid */
  838.         childStart += child.visualFootprint;
  839.     }
  840.  
  841.     if (0) {
  842.     /* XXX take this out later */
  843.     ASSERT (0, "locateChildByVisualRow() failed.  Asked for row " + targetRow +
  844.             ", record only contains " + myRow + "..." +
  845.             (myRow + this.visualFootprint));
  846.     }
  847.     return null;
  848. }   
  849.  
  850. /* TOLabelRecords can be used to drop a label into an arbitrary place in an
  851.  * arbitrary tree.  normally, specializations of TreeOViewRecord are tied to
  852.  * a specific tree because of implementation details.  TOLabelRecords are
  853.  * specially designed (err, hacked) to work around these details.  this makes
  854.  * them slower, but more generic.
  855.  *
  856.  * we set up a getter for _share that defers to the parent object.  this lets
  857.  * TOLabelRecords work in any tree.
  858.  */
  859. function TOLabelRecord (columnName, label, property)
  860. {
  861.     this.setColumnPropertyName (columnName, "label");
  862.     this.label = label;
  863.     this.property = property;
  864. }
  865.  
  866. TOLabelRecord.prototype = new TreeOViewRecord (null);
  867.  
  868. TOLabelRecord.prototype.__defineGetter__("_share", tolr_getshare);
  869. function tolr_getshare()
  870. {
  871.     if (this.parentRecord)
  872.         return this.parentRecord._share;
  873.     else
  874.     {
  875.         ASSERT (0, "TOLabelRecord cannot be the root of a visible tree.");
  876.         return null;
  877.     }
  878. }
  879.  
  880. /* TORootRecord is used internally by TreeOView, you probably don't need to make
  881.  * any of these */ 
  882. function TORootRecord (tree, share)
  883. {
  884.     this._share = share;
  885.     this._treeView = tree;
  886.     this.visualFootprint = 0;
  887.     this.isHidden = false;
  888.     this.reserveChildren();
  889.     this.isContainerOpen = true;
  890. }
  891.  
  892. /* no cache passed in here, we set it in the TORootRecord contructor instead. */
  893. TORootRecord.prototype = new TreeOViewRecord (null);
  894.  
  895. TORootRecord.prototype.open =
  896. TORootRecord.prototype.close =
  897. function torr_notimplemented()
  898. {
  899.     /* don't do this on a root node */
  900. }
  901.  
  902. TORootRecord.prototype.calculateVisualRow =
  903. function torr_calcrow ()
  904. {
  905.     return null;
  906. }
  907.  
  908. TORootRecord.prototype.resort =
  909. function torr_resort ()
  910. {
  911.     if (!("childData" in this) || this.childData.length < 1 ||
  912.         (this.childData[0].sortCompare == tovr_sortcmp &&
  913.          !("sortColumn" in this._share) || this._share.sortDirection == 0))
  914.     {
  915.         /* if we have no children, or we have the default sort compare but we're
  916.          * missing a sort flag, then just exit */
  917.         return;
  918.     }
  919.     
  920.     this.childData.sort(this.childData[0].sortCompare);
  921.     
  922.     for (var i = 0; i < this.childData.length; ++i)
  923.     {
  924.         this.childData[i].childIndex = i;
  925.         if ("isContainerOpen" in this.childData[i] &&
  926.             this.childData[i].isContainerOpen)
  927.             this.childData[i].resort(true);
  928.         else
  929.             this.childData[i].sortIsInvalid = true;
  930.     }
  931.     
  932.     if ("_treeView" in this && "tree" in this._treeView)
  933.     {
  934.         /*
  935.         dd ("root node: invalidating 0 - " + this.visualFootprint +
  936.             " for sort");
  937.         */
  938.         this.invalidateCache();
  939.         this._treeView.tree.invalidateRange (0, this.visualFootprint);
  940.     }
  941. }
  942.  
  943. TORootRecord.prototype.locateChildByVisualRow =
  944. function torr_find (targetRow)
  945. {
  946.     if (targetRow in this._share.rowCache)
  947.         return this._share.rowCache[targetRow];
  948.  
  949.     var childStart = -1; /* childStart represents the starting visual row
  950.                           * for the child we're examining. */
  951.     for (var i = 0; i < this.childData.length; ++i)
  952.     {
  953.         var child = this.childData[i];
  954.         /* ignore hidden children */
  955.         if (child.isHidden)
  956.             continue;
  957.         /* if this kid is the targetRow, we're done */
  958.         if (childStart == targetRow)
  959.             return (this._share.rowCache[targetRow] = child);
  960.         /* if this kid contains the index, ask *it* to find the record */
  961.         else if (targetRow <= childStart + child.visualFootprint) {
  962.             /* this *has* to succeed */
  963.             var rv = child.locateChildByVisualRow(targetRow, childStart + 1);
  964.             //XXXASSERT (rv, "Can't find a row that *has* to be there.");
  965.             /* don't cache this, the previous call to locateChildByVisualRow
  966.              * just did. */
  967.             return rv;
  968.         }
  969.  
  970.         /* otherwise, get ready to ask the next kid */
  971.         childStart += child.visualFootprint;
  972.     }
  973.  
  974.     return null;
  975. }
  976.  
  977. TORootRecord.prototype.onVisualFootprintChanged =
  978. function torr_vfpchange (start, amount)
  979. {
  980.     this.invalidateCache();
  981.     this.visualFootprint += amount;
  982.     if ("_treeView" in this && "tree" in this._treeView)
  983.     {
  984.         if (amount != 0)
  985.             this._treeView.tree.rowCountChanged (start, amount);
  986.         else
  987.             this._treeView.tree.invalidateRow (start);
  988.     }
  989. }
  990.  
  991. /*
  992.  * TreeOView provides functionality of tree whose elements have multiple
  993.  * levels of children.
  994.  */
  995.  
  996. function TreeOView(share)
  997. {
  998.     this.childData = new TORootRecord(this, share);
  999.     this.childData.invalidateCache();
  1000. }
  1001.  
  1002. /* functions *you* should call to initialize and maintain the tree state */
  1003.  
  1004. /* scroll the line specified by |line| to the center of the tree */
  1005. TreeOView.prototype.centerLine =
  1006. function bov_ctrln (line)
  1007. {
  1008.     var first = this.tree.getFirstVisibleRow();
  1009.     var last = this.tree.getLastVisibleRow();
  1010.     this.scrollToRow(line - total / 2);
  1011. }
  1012.  
  1013. /*
  1014.  * functions the tree will call to retrieve the list state (nsITreeView.)
  1015.  */
  1016.  
  1017. TreeOView.prototype.__defineGetter__("rowCount", tov_getRowCount);
  1018. function tov_getRowCount ()
  1019. {
  1020.     return this.childData.visualFootprint;
  1021. }
  1022.  
  1023. TreeOView.prototype.isContainer =
  1024. function tov_isctr (index)
  1025. {
  1026.     var row = this.childData.locateChildByVisualRow (index);
  1027.     /*
  1028.     ASSERT(row, "bogus row");
  1029.     var rv = Boolean(row && row.childData);
  1030.     dd ("isContainer: row " + index + " returning " + rv);
  1031.     return rv;
  1032.     */
  1033.  
  1034.     return Boolean(row && row.childData);
  1035. }
  1036.  
  1037. TreeOView.prototype.__defineGetter__("selectedIndex", tov_getsel);
  1038. function tov_getsel()
  1039. {
  1040.     if (this.tree.view.selection.getRangeCount() < 1)
  1041.         return -1;
  1042.  
  1043.     var min = new Object();
  1044.     this.tree.view.selection.getRangeAt(0, min, {});
  1045.     return min.value;
  1046. }
  1047.  
  1048. TreeOView.prototype.__defineSetter__("selectedIndex", tov_setsel);
  1049. function tov_setsel(i)
  1050. {
  1051.     this.tree.view.selection.timedSelect (i, 500);
  1052.     return i;
  1053. }
  1054.  
  1055. TreeOView.prototype.scrollTo = BasicOView.prototype.scrollTo;
  1056.  
  1057. TreeOView.prototype.isContainerOpen =
  1058. function tov_isctropen (index)
  1059. {
  1060.     var row = this.childData.locateChildByVisualRow (index);
  1061.     /*
  1062.     ASSERT(row, "bogus row");
  1063.     var rv = Boolean(row && row.isContainerOpen);
  1064.     dd ("isContainerOpen: row " + index + " returning " + rv);
  1065.     return rv;
  1066.     */
  1067.     return row && row.isContainerOpen;
  1068. }
  1069.  
  1070. TreeOView.prototype.toggleOpenState =
  1071. function tov_toggleopen (index)
  1072. {
  1073.     var row = this.childData.locateChildByVisualRow (index);
  1074.     //ASSERT(row, "bogus row");
  1075.     if (row)
  1076.     {
  1077.         if (row.isContainerOpen)
  1078.             row.close();
  1079.         else
  1080.             row.open();
  1081.     }
  1082. }
  1083.  
  1084. TreeOView.prototype.isContainerEmpty =
  1085. function tov_isctrempt (index)
  1086. {
  1087.     var row = this.childData.locateChildByVisualRow (index);
  1088.     /*
  1089.     ASSERT(row, "bogus row");
  1090.     var rv = Boolean(row && (row.childData.length == 0));
  1091.     dd ("isContainerEmpty: row " + index + " returning " + rv);
  1092.     return rv;
  1093.     */
  1094.     return !row || !row.childData;
  1095. }
  1096.  
  1097. TreeOView.prototype.getParentIndex =
  1098. function tov_getpi (index)
  1099. {
  1100.     var row = this.childData.locateChildByVisualRow (index);
  1101.     //ASSERT(row, "bogus row " + index);
  1102.     var rv = row.parentRecord.calculateVisualRow();
  1103.     //dd ("getParentIndex: row " + index + " returning " + rv);
  1104.     return (rv != null) ? rv : -1;
  1105. }
  1106.  
  1107. TreeOView.prototype.hasNextSibling =
  1108. function tov_hasnxtsib (rowIndex, afterIndex)
  1109. {
  1110.     var row = this.childData.locateChildByVisualRow (rowIndex);
  1111.     /*
  1112.     ASSERT(row, "bogus row");
  1113.     rv = Boolean(row.childIndex < row.parentRecord.childData.length - 1);
  1114.     dd ("hasNextSibling: row " + rowIndex + ", after " + afterIndex +
  1115.         " returning " + rv);
  1116.     return rv;
  1117.     */
  1118.     return row.childIndex < row.parentRecord.childData.length - 1;
  1119. }
  1120.  
  1121. TreeOView.prototype.getLevel =
  1122. function tov_getlvl (index)
  1123. {
  1124.     var row = this.childData.locateChildByVisualRow (index);
  1125.     /*
  1126.     ASSERT(row, "bogus row");
  1127.     var rv = row.level;
  1128.     dd ("getLevel: row " + index + " returning " + rv);
  1129.     return rv;
  1130.     */
  1131.     if (!row)
  1132.         return 0;
  1133.     
  1134.     return row.level;
  1135. }
  1136.  
  1137. TreeOView.prototype.getImageSrc =
  1138. function tov_getimgsrc (index, col)
  1139. {
  1140. }
  1141.  
  1142. TreeOView.prototype.getProgressMode =
  1143. function tov_getprgmode (index, col)
  1144. {
  1145. }
  1146.  
  1147. TreeOView.prototype.getCellValue =
  1148. function tov_getcellval (index, col)
  1149. {
  1150. }
  1151.  
  1152. TreeOView.prototype.getCellText =
  1153. function tov_getcelltxt (index, col)
  1154. {
  1155.     var row = this.childData.locateChildByVisualRow (index);
  1156.     //ASSERT(row, "bogus row " + index);
  1157.     if (row._colValues)
  1158.         return row._colValues[col.id];
  1159.     else
  1160.         return null;
  1161. }
  1162.  
  1163. TreeOView.prototype.getCellProperties =
  1164. function tov_cellprops (row, col, properties)
  1165. {}
  1166.  
  1167. TreeOView.prototype.getColumnProperties =
  1168. function tov_colprops (col, properties)
  1169. {}
  1170.  
  1171. TreeOView.prototype.getRowProperties =
  1172. function tov_rowprops (index, properties)
  1173. {}
  1174.  
  1175. TreeOView.prototype.isSeparator =
  1176. function tov_isseparator (index)
  1177. {
  1178.     return false;
  1179. }
  1180.  
  1181. TreeOView.prototype.isSorted =
  1182. function tov_issorted (index)
  1183. {
  1184.     return false;
  1185. }
  1186.  
  1187. TreeOView.prototype.canDrop =
  1188. function tov_dropon (index, orientation)
  1189. {
  1190.     var row = this.childData.locateChildByVisualRow (index);
  1191.     //ASSERT(row, "bogus row " + index);
  1192.     return (row && ("canDrop" in row) && row.canDropOn(orientation));
  1193. }
  1194.  
  1195. TreeOView.prototype.drop =
  1196. function tov_drop (index, orientation)
  1197. {
  1198.     var row = this.childData.locateChildByVisualRow (index);
  1199.     //ASSERT(row, "bogus row " + index);
  1200.     return (row && ("drop" in row) && row.drop(orientation));
  1201. }
  1202.  
  1203. TreeOView.prototype.setTree =
  1204. function tov_seto (tree)
  1205. {
  1206.     this.tree = tree;
  1207. }
  1208.  
  1209. TreeOView.prototype.cycleHeader =
  1210. function tov_cyclehdr (col)
  1211. {
  1212. }
  1213.  
  1214. TreeOView.prototype.selectionChanged =
  1215. function tov_selchg ()
  1216. {
  1217. }
  1218.  
  1219. TreeOView.prototype.cycleCell =
  1220. function tov_cyclecell (row, col)
  1221. {
  1222. }
  1223.  
  1224. TreeOView.prototype.isEditable =
  1225. function tov_isedit (row, col)
  1226. {
  1227.     return false;
  1228. }
  1229.  
  1230. TreeOView.prototype.isSelectable =
  1231. function tov_isselect (row, col)
  1232. {
  1233.     return false;
  1234. }
  1235.  
  1236. TreeOView.prototype.setCellValue =
  1237. function tov_setct (row, col, value)
  1238. {
  1239. }
  1240.  
  1241. TreeOView.prototype.setCellText =
  1242. function tov_setct (row, col, value)
  1243. {
  1244. }
  1245.  
  1246. TreeOView.prototype.performAction =
  1247. function tov_pact (action)
  1248. {
  1249. }
  1250.  
  1251. TreeOView.prototype.performActionOnRow =
  1252. function tov_pactrow (action)
  1253. {
  1254. }
  1255.  
  1256. TreeOView.prototype.performActionOnCell =
  1257. function tov_pactcell (action)
  1258. {
  1259. }
  1260.